<!doctype html>
<html lang="en-US">

<head>
  <title>Part grouping: position</title>
  <link href="/assets/index.css" rel="stylesheet" type="text/css" />
  <script type="importmap">
      {
        "imports": {
          "react": "https://esm.sh/react@18.3.1",
          "react-dom": "https://esm.sh/react-dom@18.3.1",
          "react-dom/": "https://esm.sh/react-dom@18.3.1/",
          "@testduet/wait-for": "https://unpkg.com/@testduet/wait-for@main/dist/wait-for.mjs",
          "@fluentui/react-components": "https://esm.sh/@fluentui/react-components?deps=react@18.3.1&exports=FluentProvider,createDarkTheme,webLightTheme"
        }
      }
    </script>
  <script crossorigin="anonymous" src="/test-harness.js"></script>
  <script crossorigin="anonymous" src="/test-page-object.js"></script>
  <script type="module">
    import React from 'react';
    window.React = React;
  </script>
  <script defer crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
  <script defer crossorigin="anonymous" src="/__dist__/botframework-webchat-fluent-theme.development.js"></script>
  <style type="text/css">
    #webchat {
      width: 640px;
    }

    .fui-FluentProvider {
      height: 100%;
    }

    .theme.variant-copilot-deprecated {
      --webchat__color--surface: var(--colorGrey98);
    }
  </style>
</head>

<body>
  <main id="webchat"></main>
  <script type="module">
    import React from 'react';
    import { createRoot } from 'react-dom/client';
    import { FluentProvider, createDarkTheme, webLightTheme } from '@fluentui/react-components';
    import { waitFor } from '@testduet/wait-for';

    run(async function () {
      const {
        WebChat: { FluentThemeProvider, ReactWebChat }
      } = window;

      const { directLine, store } = testHelpers.createDirectLineEmulator();

      const searchParams = new URLSearchParams(location.search);
      const variant = searchParams.get('variant');
      const theme = searchParams.get('fluent-theme');

      await host.windowSize(640, 1024, document.getElementById('webchat'));
      await host.sendDevToolsCommand('Emulation.setEmulatedMedia', {
        features: [
          { name: 'prefers-reduced-motion', value: 'reduce' },
          ...(theme === 'dark' || theme === 'light'
            ? [{ name: 'prefers-color-scheme', value: theme }]
            : [])
        ]
      });

      const root = createRoot(document.getElementById('webchat'));

      let fluentTheme;
      let codeBlockTheme;

      if (theme === 'dark' || window.matchMedia('(prefers-color-scheme: dark)').matches && theme !== 'light') {
        fluentTheme = {
          ...createDarkTheme({
            10: '#12174c',
            20: '#1a1f5b',
            30: '#21276a',
            40: '#293079',
            50: '#303788',
            60: '#384097',
            70: '#4049a7',
            80: '#151e80',
            90: '#4f59c5',
            100: '#5661d4',
            110: '#5e69e3',
            120: '#7982e8',
            130: '#949bec',
            140: '#afb5f1',
            150: '#c9cdf6',
            160: '#e4e6fa'
          }),
          colorNeutralBackground1Disabled: '#101010',
          colorNeutralBackground1Hover: '#101010',
          colorNeutralForeground5: '#424242',
          colorGrey98: '#1b1b1b'
        };
        codeBlockTheme = 'github-dark-default';
      } else {
        fluentTheme = {
          ...webLightTheme,
          // Original is #242424 which is too light for fui-FluentProvider to pass our a11y
          colorNeutralForeground1: '#1b1b1b',
          colorGrey98: '#fafafa'
        };
        codeBlockTheme = 'github-light-default';
      }

      // TODO: code block github theme triggers accessibility violation
      if (variant) {
        window.checkAccessibility = async () => { }
      }

      const webChatProps = { directLine, store, styleOptions: { codeBlockTheme } };

      root.render(
        variant ?
          React.createElement(
            FluentProvider,
            { className: 'fui-FluentProvider', theme: fluentTheme },
            React.createElement(
              FluentThemeProvider,
              { variant: variant },
              React.createElement(ReactWebChat, webChatProps)
            )
          ) :
          React.createElement(ReactWebChat, webChatProps)
      );

      await pageConditions.uiConnected();

      const botIcon = '';

      const aiMessageEntity = {
        '@context': 'https://schema.org',
        '@id': '',
        '@type': 'Message',
        keywords: ['AIGeneratedContent', 'AnalysisMessage'],
        type: 'https://schema.org/Message',
        author: {
          '@context': 'https://schema.org',
          '@type': 'Person',
          image: botIcon,
          name: 'Research',
        },
        isPartOf: {
          '@id': '_:h-00001',
          '@type': 'HowTo',
        }
      };

      const withPosition = (activity, position, abstract) => ({
        ...activity,
        entities: activity.entities.map(entity => ({
          ...entity,
          position,
          abstract
        }))
      });

      directLine.emulateIncomingActivity({
        from: { role: 'user' },
        type: 'message',
        text: `Message from user.`
      });

      const activities = [
        {
          entities: [
            {
              ...aiMessageEntity,
            }
          ],
          id: 'a-00001'
        },
        {
          entities: [
            {
              ...aiMessageEntity,
            }
          ],
          id: 'a-00002'
        },
        {
          entities: [
            {
              ...aiMessageEntity,
            }
          ],
          id: 'a-00003'
        },
        {
          entities: [
            {
              ...aiMessageEntity,
            }
          ],
          id: 'a-00004'
        },
        {
          entities: [
            {
              ...aiMessageEntity,
            }
          ],
          id: 'a-00005'
        },
      ];

      // Test Case 1: Initial activities with positions
      // When: adding activities with specific positions
      directLine.emulateIncomingActivity(withPosition(activities.at(0), 2, 'two'));
      directLine.emulateIncomingActivity(withPosition(activities.at(1), 4, 'four'));

      // Then: show activities positioned according to their position values
      await pageConditions.numActivitiesShown(3);
      await host.snapshot('local');

      // Test Case 2: Insertion before existing activities
      // When: adding an activity with position 1 (before existing position 2)
      directLine.emulateIncomingActivity(withPosition(activities.at(2), 1, 'one'));

      // Then: show the new activity inserted before the existing ones
      await pageConditions.numActivitiesShown(4);
      await host.snapshot('local');

      // Test Case 3: Insertion after existing activities
      // When: adding an activity with position 5 (after existing position 4)
      directLine.emulateIncomingActivity(withPosition(activities.at(3), 5, 'five'));

      // Then: show the new activity inserted after the existing ones
      await pageConditions.numActivitiesShown(5);
      await host.snapshot('local');

      // Test Case 4: Insertion between existing activities
      // When: adding an activity with position 3 (between positions 2 and 4)
      directLine.emulateIncomingActivity(withPosition(activities.at(4), 3, 'three'));

      // Then: show the new activity inserted between existing activities
      await pageConditions.numActivitiesShown(6);
      await host.snapshot('local');

      // Test Case 5: Update position from none set
      // When: updating an existing activity to remove its position
      directLine.emulateIncomingActivity(withPosition(activities.at(0), undefined, 'two updated to no position'));

      // Then: show the activity unmoved
      await pageConditions.numActivitiesShown(6);
      await host.snapshot('local');

      // Test Case 6: Update position from set position to different position
      // When: updating an activity's position from 3 to 1
      directLine.emulateIncomingActivity(withPosition(activities.at(4), 1, 'three updated to one'));

      // Then: show the activity moved before "four"
      await pageConditions.numActivitiesShown(6);
      await host.snapshot('local');

      // Test Case 7: Update position from set position to another set position
      // When: updating an activity's position from 5 to 2
      directLine.emulateIncomingActivity(withPosition(activities.at(3), 2, 'five updated to two'));

      // Then: show the activity moved before "four"
      await pageConditions.numActivitiesShown(6);
      await host.snapshot('local');

      // Test Case 8: Multiple position updates
      // When: updating multiple activities' positions simultaneously
      directLine.emulateIncomingActivity(withPosition(activities.at(1), 6, 'four updated to six'));
      directLine.emulateIncomingActivity(withPosition(activities.at(2), 3, 'one updated to three'));

      // Then: show the "one" activity moved before "foru"
      await pageConditions.numActivitiesShown(6);
      await host.snapshot('local');
    });
  </script>
</body>

</html>
